/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "python_obj.hh"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// REFERENCE_CLASS
reference_class::reference_class()
{
    refcnt=1;
    reserved=12345678;
    dtor_error=0;

    // use embed classes
    sample_embed1 *emb=new sample_embed1;
    emb->emb.emb.emb.reserved=7;
    delete emb;
}

reference_class::~reference_class()
{
    if(refcnt!=0 && dtor_error) fprintf(stderr, "ERROR: refcnt %d != 0\n", refcnt);
}

reference_class *reference_class::decref()
{
    if(!--refcnt) {delete this; return NULL;}
    else if(refcnt<0) fprintf(stderr, "ERROR: refcnt < 0\n");
    return this;
}

int reference_class::get_refcnt()
{
    return refcnt;
}

void reference_class::show_dtor_refcnt_error()
{
    dtor_error++;
}

void reference_class::declare_local_or_member()
{
    refcnt=0;
}

int reference_class::virtual_method(int x)
{
    return x>>1;
}

// SAMPLE_CLASS
sample_class::sample_class()
{
    payload=1;
}

sample_class::sample_class(int x, int y)
{
    payload=2;
}

sample_class::~sample_class()
{
}

int sample_class::virtual_method(int x)
{
    return x<<1;
}

// SPECIALIZED_CLASS
specialized_class::specialized_class()
{
}

specialized_class::~specialized_class()
{
}

int specialized_class::virtual_method(int x)
{
    return x*3;
}


// CONTAINER_CLASS
container_class::container_class()
{
    memset(&sample_str_obj, 0, sizeof(sample_struct));
    sample_str_objp=NULL;
    sample_objp=NULL;
    sample_obj.declare_local_or_member();
    refobj=NULL;
}

container_class::~container_class()
{
    if(sample_objp) sample_objp->decref();
    if(sample_str_objp) delete sample_str_objp;
}

int container_class::get_sample_objp_refcnt()
{
    if(sample_objp) return sample_objp->get_refcnt();
    return 0;
}

void container_class::pass_objp(sample_class *objp)
{
    // do nothing
}

void container_class::pass_objp(sample_struct *objp)
{
    // do nothing
}

sample_class *container_class::get_sample_class()
{
    if(sample_objp) sample_objp->incref();
    return sample_objp;
}

sample_struct *container_class::get_sample_struct1()
{
    return sample_str_objp;
}

sample_struct *container_class::get_sample_struct2()
{
    return &sample_str_obj;
}

void container_class::new_sample_objp()
{
    if(!sample_objp) sample_objp=new sample_class;
}

void container_class::new_sample_str_objp()
{
    if(!sample_str_objp) sample_str_objp=new sample_struct;
}

sample_struct &container_class::refret_struct1()
{
    return sample_str_obj;
}

sample_struct &container_class::refret_struct2()
{
    return *sample_str_objp;
}

// P3 EMBED
sample_embed4::sample_embed4()
{
}

// reference counts in detail
reference_counter::reference_counter()
{
    refcnt=1;
}

reference_counter::~reference_counter()
{
    if(refcnt) printf("reference count (=%d) should be zero: %p\n", refcnt, this);
}

reference_counter *reference_counter::decref()
{
    if(!--refcnt)
    {
	// must delete object
	delete this;
	return NULL;
    }
    else if(refcnt<0) printf("reference count (=%d) below zero: %p\n", refcnt, this);
    return this;
}

counted1::counted1()
{
    c1_member=-123;
    c1_cad=NULL;
}

counted1::~counted1()
{
}

void counted1::init(char *cad)
{
    if(c1_cad) free(c1_cad);
    c1_cad=cad;
}

counted2::counted2()
{
    c2_member=987;
}

counted2::~counted2()
{
}

counted1 *counted2::new_counted1()
{
    counted1 *c1=new counted1;
    c1->init(const_cast<char*>("hello"));
    return c1;
}

// object_array
array_container::array_container()
{
    memset(this, 0, sizeof(*this));
}

int array_container::get_value(int x, int y)
{
    return array_object[x].inner_array_object[y].value;
}

void array_container::set_value(int x, int y, int value)
{
    array_object[x].inner_array_object[y].value=value;
}

// vector of objects
object_vector::object_vector()
{
    v=NULL;
    n_v=max_v=0;

    w=NULL;
    n_w=max_w=0;
}

object_vector::~object_vector()
{
    if(v) v=(obj*)realloc(v, 0);
    if(w) w=(obj**)realloc(w, 0);
}

void object_vector::add_v()
{
    int i=n_v++;
    if(n_v>max_v)
    {
	max_v+=16;
	v=(obj*)realloc(v, max_v*sizeof(obj));
    }
    v[i].x=i;
    v[i].y=(double)i;
    v[i].z=true;
    for(int j=0; j<12; j++) v[i].v[j]=j;
}

void object_vector::add_w()
{
    int i=n_w++;
    if(n_w>max_w)
    {
	max_w+=16;
	w=(obj**)realloc(w, max_w*sizeof(obj*));
    }
    w[i]=new obj;
    w[i]->x=i;
    w[i]->y=(double)i;
    w[i]->z=true;
    for(int j=0; j<12; j++) w[i]->v[j]=j;
}

// P4 embedded
p4_1::p4_1()
{
    x=123;
}

p4_1::~p4_1()
{
}

p4_2::p4_2()
{
    p=new p4_1();
}

p4_2::~p4_2()
{
    delete p;
}

p4_3::p4_3()
{
    p=new p4_2();
}

p4_3::~p4_3()
{
    del_p();
}

void p4_3::del_p()
{
    if(p)
    {
	delete p;
	p=NULL;
    }
}
